/*
 * MIT许可证
 *
 * 版权所有 (c) 2025-2035 JacobGreen
 *
 * 特此免费授予获得本软件及相关文档文件（"软件"）副本的任何人不受限制地处置该软件的权利，
 * 包括不限于使用、复制、修改、合并、发布、分发、再许可和/或出售该软件副本，
 * 以及允许被提供该软件的人这样做，须遵守以下条件：
 *
 * 上述版权声明和本许可声明应包含在该软件的所有副本或实质性部分中。
 *
 * 本软件按"原样"提供，不附带任何形式的明示或暗示的保证，
 * 包括但不限于对适销性、特定用途适用性和不侵权的保证。
 * 在任何情况下，作者或版权持有人均不对任何索赔、损害或其他
 * 责任负责，无论是在合同行为、侵权行为或其他方面，
 * 与软件或软件的使用或其他交易有关。
 */

/**
 * filename:Log.c
 * description:是日志系统的实现
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#else
#include <sys/stat.h>
#endif
#include "Log.h"


// 全局日志级别
static LogLevel currentLevel = LOG_DEBUG;

// 日志合并标志，默认为TRUE（合并）
static int logMergeEnabled = 1;

// 文件指针
static FILE *debugFile = NULL;
static FILE *infoFile = NULL;
static FILE *warnFile = NULL;
static FILE *errorFile = NULL;
static FILE *mergedLogFile = NULL;

// 日志目录路径
#define LOG_DIR "./logs/"

// 日志文件路径
#define DEBUG_LOG_FILE LOG_DIR "debug.log"
#define INFO_LOG_FILE LOG_DIR "info.log"
#define WARN_LOG_FILE LOG_DIR "warn.log"
#define ERROR_LOG_FILE LOG_DIR "error.log"
#define MERGED_LOG_FILE LOG_DIR "c-core.log"

/**
 * <p>
 * 创建目录（跨平台）
 * </p>
 *
 * @param path 目录路径
 * @return 成功返回SUCCESS，失败返回FAIL
 */
static bool createDirectory(const char *path)
{
// 判断path是否存在，存在则不需要创建，直接返回成功
#ifdef _WIN32
    // Windows平台使用_access函数检查目录是否存在
    if (_access(path, 0) == 0)
    {
        // 检查是否是目录
        DWORD fileAttr = GetFileAttributesA(path);
        if (fileAttr != INVALID_FILE_ATTRIBUTES && (fileAttr & FILE_ATTRIBUTE_DIRECTORY))
        {
            printf("[WARN] [日志] [创建目录] - Log.c[84] - 目录'%s'已存在，不需要创建。\n", LOG_DIR);
            return true;
        }
    }
    // 在Windows上使用_mkdir函数创建目录
    printf("[INFO] [日志] [创建目录] - Log.c[89] - 目录'%s'创建成功。\n", LOG_DIR);
    return _mkdir(path) == 0 ? true : false;
#else
    // Linux/macOS平台使用stat函数检查目录是否存在
    struct stat st;
    if (stat(path, &st) == 0)
    {
        if (S_ISDIR(st.st_mode))
        {
            printf("[WARN] [日志] [创建目录] - Log.c[98] - 目录'%s'已存在，不需要创建。\n", LOG_DIR);
            return true;
        }
    }
    // 在Linux/macOS上使用mkdir函数创建目录
    printf("[INFO] [日志] [创建目录] - Log.c[103] - 目录'%s'创建成功。\n", LOG_DIR);
    return mkdir(path, 0777) == 0 ? true : false;
#endif
}

/**
 * <p>
 * 获取当前时间的格式化字符串
 * </p>
 *
 * @param buffer 输出缓冲区
 * @param size 缓冲区大小
 * @param includeMs 是否包含毫秒
 */
static void getCurrentTime(char *buffer, size_t size, int includeMs)
{
    time_t now;
    struct tm *localTime;
    struct timespec ts;
    int millisec;

    time(&now);
    localTime = localtime(&now);

// 获取毫秒级时间
#ifdef _WIN32
    // Windows平台获取毫秒
    millisec = (int)(GetTickCount() % 1000);
#else
    // Linux/Unix平台获取纳秒并转换为毫秒
    clock_gettime(CLOCK_REALTIME, &ts);
    millisec = (int)((ts.tv_nsec / 1000000) % 1000);
#endif

    // 格式化时间字符串：yyyy-MM-dd HH:mm:ss SSS
    strftime(buffer, size, "%Y-%m-%d %H:%M:%S", localTime);
    snprintf(buffer + strlen(buffer), size - strlen(buffer), " %03d", millisec);
}

/**
 * <p>
 * 获取日志级别对应的字符串
 * </p>
 *
 * @param level 日志级别
 * @return 日志级别字符串
 */
static const char *getLogLevelString(LogLevel level)
{
    switch (level)
    {
    case LOG_DEBUG:
        return "DEBUG";
    case LOG_INFO:
        return "INFO";
    case LOG_WARN:
        return "WARN";
    case LOG_ERROR:
        return "ERROR";
    default:
        return "UNKNOWN";
    }
}

/**
 * 获取文件名（去掉路径）
 * @param filePath 文件路径
 * @return 文件名
 */
static const char *getFileName(const char *filePath)
{
    const char *fileName = strrchr(filePath, '\\'); // Windows路径分隔符
    if (!fileName)
    {
        fileName = strrchr(filePath, '/'); // Linux路径分隔符
    }
    if (fileName)
    {
        return fileName + 1;
    }
    return filePath; // 没有路径分隔符，直接返回
}

/**
 * <p>
 * 打印日志的通用函数
 * </p>
 *
 * @param level 日志级别
 * @param module 模块名称
 * @param function 功能名称
 * @param file 源文件名称
 * @param line 行号
 * @param format 格式化字符串
 * @param args 可变参数列表
 */
static void logMessage(LogLevel level, const char *module, const char *function,
                       const char *file, int line, const char *format, va_list args)
{
    if (level < currentLevel)
    {
        return; // 日志级别低于全局级别，不输出
    }

    char timeBuffer[30];
    char logBuffer[2048];
    char contentBuffer[1024];
    FILE *targetFile = NULL;

    // 获取当前时间
    getCurrentTime(timeBuffer, sizeof(timeBuffer), 1);

    // 格式化日志内容
    vsnprintf(contentBuffer, sizeof(contentBuffer), format, args);

    // 组装完整日志行
    const char *fileName = getFileName(file);
    snprintf(logBuffer, sizeof(logBuffer),
             "%s [%s] [%s] [%s] - %s[%d] - %s\n",
             timeBuffer,
             getLogLevelString(level),
             module,
             function,
             fileName,
             line,
             contentBuffer);

    // 如果启用了日志合并，则写入合并日志文件
    if (logMergeEnabled)
    {
        if (mergedLogFile)
        {
            fprintf(mergedLogFile, "%s", logBuffer);
            fflush(mergedLogFile);
        }
        return; // 如果启用了合并模式，只写入合并文件并返回
    }

    // 否则，根据日志级别选择目标文件（非合并模式）
    switch (level)
    {
    case LOG_DEBUG:
        targetFile = debugFile;
        break;
    case LOG_INFO:
        targetFile = infoFile;
        break;
    case LOG_WARN:
        targetFile = warnFile;
        break;
    case LOG_ERROR:
        targetFile = errorFile;
        break;
    default:
        return;
    }

    // 写入日志到文件
    if (targetFile)
    {
        fprintf(targetFile, "%s", logBuffer);
        fflush(targetFile);
    }
}

/**
 * <p>
 * 初始化日志系统
 * </p>
 *
 * @return 成功返回SUCCESS，失败返回FAIL
 */
bool logInit()
{
    // 创建日志目录
    if (!createDirectory(LOG_DIR))
    {
        printf("[ERROR] [日志] [初始化] - Log.c[280] - 日志目录'%s'创建失败，日志系统初始化失败。\n", LOG_DIR);
        fprintf(stderr, "[ERROR] 日志目录'%s'创建失败，日志系统初始化失败。\n", LOG_DIR);
        return false;
    }

    // 日志合并模式
    if (logMergeEnabled)
    {
        // 合并模式：只打开合并日志文件
        mergedLogFile = fopen(MERGED_LOG_FILE, "a");
        if (!mergedLogFile)
        {
            printf("[ERROR] [日志] [初始化] - Log.c[292] - 日志文件'%s'追加写入失败，日志系统初始化失败。\n", MERGED_LOG_FILE);
            fprintf(stderr, "[ERROR] 日志文件'%s'追加写入失败，日志系统初始化失败。\n", MERGED_LOG_FILE);
            logClose();
            return false;
        }
    }
    else
    {
        // 非合并模式：打开各个级别日志文件
        debugFile = fopen(DEBUG_LOG_FILE, "a");
        if (!debugFile)
        {
            printf("[ERROR] [日志] [初始化] - Log.c[304] - 日志文件'%s'追加写入失败，日志系统初始化失败。\n", DEBUG_LOG_FILE);
            fprintf(stderr, "[ERROR] 日志文件'%s'追加写入失败，日志系统初始化失败。\n", DEBUG_LOG_FILE);
        }
        infoFile = fopen(INFO_LOG_FILE, "a");
        if (!infoFile)
        {
            printf("[ERROR] [日志] [初始化] - Log.c[310] - 日志文件'%s'追加写入失败，日志系统初始化失败。\n", INFO_LOG_FILE);
            fprintf(stderr, "[ERROR] 日志文件'%s'追加写入失败，日志系统初始化失败。\n", INFO_LOG_FILE);
        }
        warnFile = fopen(WARN_LOG_FILE, "a");
        if (!warnFile)
        {
            printf("[ERROR] [日志] [初始化] - Log.c[316] - 日志文件'%s'追加写入失败，日志系统初始化失败。\n", WARN_LOG_FILE);
            fprintf(stderr, "[ERROR] 日志文件'%s'追加写入失败，日志系统初始化失败。\n", WARN_LOG_FILE);
        }
        errorFile = fopen(ERROR_LOG_FILE, "a");
        if (!errorFile)
        {
            printf("[ERROR] [日志] [初始化] - Log.c[322] - 日志文件'%s'追加写入失败，日志系统初始化失败。\n", ERROR_LOG_FILE);
            fprintf(stderr, "[ERROR] 日志文件'%s'追加写入失败，日志系统初始化失败。\n", ERROR_LOG_FILE);
        }
        // 检查文件是否成功打开
        if (!debugFile || !infoFile || !warnFile || !errorFile)
        {
            logClose(); // 关闭已打开的文件
            return false;
        }
    }
    return true;
}

/**
 * <p>
 * 设置全局日志级别
 * </p>
 *
 * @param level 日志级别
 */
void setLogLevel(LogLevel level)
{
    // switch (level)
    // {
    // case LOG_DEBUG:
    //     printf("[INFO] [日志] [级别调整] - Log.c[347] - 日志级别调整为:DEBUG。\n");
    //     break;
    // case LOG_INFO:
    //     printf("[INFO] [日志] [级别调整] - Log.c[350] - 日志级别调整为:INFO。\n");
    //     break;
    // case LOG_WARN:
    //     printf("[INFO] [日志] [级别调整] - Log.c[353] - 日志级别调整为:WARN。\n");
    //     break;
    // case LOG_ERROR:
    //     printf("[INFO] [日志] [级别调整] - Log.c[356] - 日志级别调整为:ERROR。\n");
    //     break;
    // default:
    // }
    currentLevel = level;
}

/**
 * <p>
 * 从命令行参数设置日志合并模式
 * </p>
 *
 * @param argc 参数数量
 * @param argv 参数列表(字符串数组)
 */
void setLogMergeFromArgs(int argc, char *argv[])
{
    for (int i = 0; i < argc; i++)
    {
        if (strncmp(argv[i], "LOG_MERGE=", 10) == 0)
        {
            const char *mergeStr = argv[i] + 10;
            if (strcmp(mergeStr, "FALSE") == 0 || strcmp(mergeStr, "false") == 0)
            {
                printf("[INFO] [日志] [合并模式] - Log.c[380] - 日志合并模式为:禁用合并模式。\n");
                logMergeEnabled = 0; // 禁用日志合并
            }
            else
            {
                printf("[INFO] [日志] [合并模式] - Log.c[385] - 日志合并模式为:启用合并模式。\n");
                logMergeEnabled = 1; // 默认启用日志合并
            }
            break;
        }
    }
}

/**
 * <p>
 * 从命令行参数设置日志级别
 * </p>
 *
 * @param argc 参数数量
 * @param argv 参数列表(字符串数组)
 */
void setLogLevelFromArgs(int argc, char *argv[])
{
    for (int i = 0; i < argc; i++)
    {
        if (strncmp(argv[i], "LOG_LEVEL=", 10) == 0)
        {
            const char *levelStr = argv[i] + 10;
            if (strcmp(levelStr, "DEBUG") == 0)
            {
                setLogLevel(LOG_DEBUG);
            }
            else if (strcmp(levelStr, "INFO") == 0)
            {
                setLogLevel(LOG_INFO);
            }
            else if (strcmp(levelStr, "WARN") == 0)
            {
                setLogLevel(LOG_WARN);
            }
            else if (strcmp(levelStr, "ERROR") == 0)
            {
                setLogLevel(LOG_ERROR);
            }
            break;
        }
    }
}

/**
 * <p>
 * 关闭日志系统，释放资源
 * </p>
 */
void logClose()
{
    if (debugFile)
    {
        fclose(debugFile);
        debugFile = NULL;
    }
    if (infoFile)
    {
        fclose(infoFile);
        infoFile = NULL;
    }
    if (warnFile)
    {
        fclose(warnFile);
        warnFile = NULL;
    }
    if (errorFile)
    {
        fclose(errorFile);
        errorFile = NULL;
    }
    if (mergedLogFile)
    {
        fclose(mergedLogFile);
        mergedLogFile = NULL;
    }
}

/**
 * <p>
 * 打印调试日志
 * </p>
 *
 * @param module 模块名称
 * @param function 功能名称
 * @param file 源文件名称
 * @param line 行号
 * @param format 格式化字符串
 * @param ... 可变参数
 */
void logDebug(const char *module, const char *function, const char *file, int line, const char *format, ...)
{
    va_list args;
    va_start(args, format);
    logMessage(LOG_DEBUG, module, function, file, line, format, args);
    va_end(args);
}

/**
 * <p>
 * 打印信息日志
 * </p>
 *
 * @param module 模块名称
 * @param function 功能名称
 * @param file 源文件名称
 * @param line 行号
 * @param format 格式化字符串
 * @param ... 可变参数
 */
void logInfo(const char *module, const char *function, const char *file, int line, const char *format, ...)
{
    va_list args;
    va_start(args, format);
    logMessage(LOG_INFO, module, function, file, line, format, args);
    va_end(args);
}

/**
 * <p>
 * 打印警告日志
 * </p>
 *
 * @param module 模块名称
 * @param function 功能名称
 * @param file 源文件名称
 * @param line 行号
 * @param format 格式化字符串
 * @param ... 可变参数
 */
void logWarn(const char *module, const char *function, const char *file, int line, const char *format, ...)
{
    va_list args;
    va_start(args, format);
    logMessage(LOG_WARN, module, function, file, line, format, args);
    va_end(args);
}

/**
 * <p>
 * 打印错误日志
 * </p>
 *
 * @param module 模块名称
 * @param function 功能名称
 * @param file 源文件名称
 * @param line 行号
 * @param format 格式化字符串
 * @param ... 可变参数
 */
void logError(const char *module, const char *function, const char *file, int line, const char *format, ...)
{
    va_list args;
    va_start(args, format);
    logMessage(LOG_ERROR, module, function, file, line, format, args);
    va_end(args);
}